Почему изменения в новом Phoenix 1.3 так важны / Хабр |
您所在的位置:网站首页 › phoenix framework › Почему изменения в новом Phoenix 1.3 так важны / Хабр |
Phoenix Framework всегда был классным. Но он никогда не был таким классным, как с новым релизом 1.3 (который сейчас находится в стадии RC2). Произошло много значительных изменений. Крис МакКорд написал полный путеводитель по изменениям. Так же доступна его речь с LonestarElixir, где он подробно рассказывает про ключевые моменты. Вдохновленный его трудами, в своей статье я постараюсь рассказать вам про самые важные изменения в проекте Phoenix. Давайте начнем! Перевод выполнен самим автором оригинальной статьи Никитой Соболевым. Существующие проблемыPhoenix – новый фреймворк. И, естественно, у него есть некоторые проблемы. Основная команда работала очень старательно, чтобы решить некоторые из самых важных. Итак, каковы эти проблемы? Директория web — чистая магияПри работе над проектом с использованием Phoenix у вас есть два места для исходного кода: lib/ и web/. Концепция такова: Поместите всю свою бизнес-логику и утилиты внутрь lib/. Поместите всё, что связано с вашим веб-интерфейсом (контроллеры, представления, шаблоны) внутрь веб-каталога web/.Но понятно ли это разработчикам? Я так не думаю. Откуда появился этот веб-каталог? Это особенность Phoenix? Или другие фреймворки тоже используют его? Должен ли я использовать lib/ с Phoenix-проектами или он зарезервирован для некоторой глубинной магии? Все эти вопросы появились у меня после моей первой встречи с Phoenix. До версии 1.2 только директория web/ автоматически перезагружалась. Итак, зачем мне создавать какие-либо файлы внутри lib/ и перезапускать сервер, когда я могу поместить их где-то внутри web/ для быстрой перезагрузки? Это приводит нас к еще более важным вопросам: относятся ли мои файлы-модели (назовем их моделями в этом конкретном контексте) к web-части приложения или к основной логике? Можно ли разделить логику на разные домены или приложения (например, как в Django)? Эти вопросы остаются без ответа. Бизнес-логика в контроллерахБолее того, код шаблона, который идет в Phoenix, предполагает другой способ. Можно получить следующий код в новом проекте: defmodule Example.UserController do use Example.Web, :controller # ... def update(conn, %{"id" => id, "user" => user_params}) do user = Repo.get!(User, id) changeset = User.changeset(user, user_params) case Repo.update(changeset) do {:ok, user} -> render(conn, Example.UserView, "show.json", user: user) {:error, changeset} -> conn |> put_status(:unprocessable_entity) |> render(Example.ChangesetView, "error.json", changeset: changeset) end end endЧто должен делать разработчик, когда пользователю после успешного обновления должно быть отправлено электронное письмо? Контроллер так и просится, чтобы его расширили. Просто поставьте еще одну строку кода перед render/4, что может пойти не так? Но. Только что Phoenix сам подтолкнул нас к неправильному использованию своей кодовой базы: мы пишем бизнес логику в контроллере! На самом деле, одна дополнительная строка в контроллере это нормально. Все проблемы возникают, когда приложение растет. Таких строк становится много, приложение становится неустойчивым, неподъемным и повторяет само себя. Схемы не являются моделямиВ какой-то момент без особых причин схемы Ecto стали называться «моделями». В чем разница между «моделью» и «схемой»? Схема — это всего лишь способ определить структуру — структуру базы данных в данном конкретном случае. Модели как концепция намного сложнее схем. Модели должны обеспечивать способ управления данными и выполнять различные действия, как модели в Django или Rails. Elixir как функциональный язык не подходит для концепции «модели», поэтому они были упразднены в проекте Ecto. Файлы внутри models/ не были организованы. По мере своего роста ваше приложение становится хаотичным. Как эти файлы связаны между собой? В каком контексте мы используем их? Это было трудно понять. Кроме того, директория models/ рассматривалась как еще одно место для размещения вашей бизнес-логики, что нормально для других языков и фреймворков. Существует уже знакомая концепция «fat models». Но такая концепция, опять же, не подходит для Phoenix по уже названным причинам. РешенияС момента последнего крупного релиза многое изменилось. Самый простой способ показать все изменения — на примере. ТребованияВ этом руководстве предполагается, что у вас есть elixir-1.4, и он работает. Нет? Значит, установите его! УстановкаДля начала вам нужно будет установить новую версию Phoenix: mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez Создание нового проектаПо завершению установки надо проверить, всё ли на месте. mix help вернет вам что-то вроде этого: mix phoenix.new # Creates a new Phoenix v1.1.4 application mix phx.new # Creates a new Phoenix v1.3.0-rc.1 application using the experimental generatorsВот тут и проявляется первое изменение: новые генераторы. Старые генераторы назывались phoenix, а новые — просто phx. Теперь нужно меньше печатать. И, что более важно, новое сообщение разработчикам: эти генераторы новые, они будут делать что-то новое для вашего проекта. Затем нужно создать структуру нового проекта, запустив: mix phx.new medium_phx_example --no-html --no-brunchПрежде чем мы увидим какие-либо результаты этой команды, давайте обсудим параметры. --no-html удаляет некоторые компоненты для работы с html, поэтому phx.gen.html больше не будет работать. Но мы строим json API, и нам не нужен html. Аналогично --no-brunch означает: не создавайте brunch-файл для работы со статикой. Изменения Веб-директорияГлядя на ваши новые файлы, вы можете задаться вопросом: где находится веб-директория? Ну, вот и второе изменение. И довольно большое. Теперь ваша веб-директория находится внутри lib/. Она была особенной, многие люди неправильно поняли его главную цель, которая состояла в содержании веб-интерфейса для вашего приложения. Это не место для вашей бизнес-логики. Теперь все ясно. Поместите всё внутрь lib/. И оставьте только свои контроллеры, шаблоны и представления внутри новой web-директории. Вот как это выглядит: lib └── medium_phx_example ├── application.ex ├── repo.ex └── web ├── channels │ └── user_socket.ex ├── controllers ├── endpoint.ex ├── gettext.ex ├── router.ex ├── views │ ├── error_helpers.ex │ └── error_view.ex └── web.exГде medium_phx_example — имя текущего приложения. Приложений может быть много. Итак, теперь весь код живет в одной и той же директории. Третье изменение откроется вскоре после просмотра файла web.ex: defmodule MediumPhxExample.Web do def controller do quote do use Phoenix.Controller, namespace: MediumPhxExample.Web import Plug.Conn # Before 1.3 it was just: # import MediumPhxExample.Router.Helpers import MediumPhxExample.Web.Router.Helpers import MediumPhxExample.Web.Gettext end end # Some extra code: # ... endPhoenix теперь создает пространство имен .Web, которое очень хорошо сочетается с новой файловой структурой. Создание схемыЭто четвертое и моё любимое изменение. Раньше у нас была директория web/models/, которая использовалась для хранения схем. Теперь концепция моделей полностью мертва. Внедрена новая философия: схема представляет структуру данных; контекст используется для хранения нескольких схем; контекст используется для предоставления публичного внешнего API. Другими словами, он определяет, что можно сделать с вашими данными.Наше приложение будет содержать только один контекст: Audio. Начнем с создания Audio контекста с двумя схемами Album и Song: mix phx.gen.json Audio Album albums name:string release:utc_datetime mix phx.gen.json Audio Song songs album_id:references:audio_albums name:string duration:integerСинтаксис этого генератора также изменился. Теперь требуется, чтобы имя контекста было первым аргументом. Также обратите внимание на audio_albums, схемы теперь содержат префикс с именем контекста. И вот что происходит со структурой проекта после запуска двух генераторов: lib └── medium_phx_example ├── application.ex ├── audio │ ├── album.ex │ ├── audio.ex │ └── song.ex ├── repo.ex └── web ├── channels │ └── user_socket.ex ├── controllers │ ├── album_controller.ex │ ├── fallback_controller.ex │ └── song_controller.ex ├── endpoint.ex ├── gettext.ex ├── router.ex ├── views │ ├── album_view.ex │ ├── changeset_view.ex │ ├── error_helpers.ex │ ├── error_view.ex │ └── song_view.ex └── web.exКаковы основные изменения в структурах по сравнению с предыдущей версией? Теперь схемы не принадлежат web/, а директория models/ вообще исчезла. Схемы теперь разделены контекстом, который определяет, как они связаны друг с другом.И схемы прямо сейчас являются не более чем описанием таблицы. Чем и должна быть схема в первую очередь. Вот как выглядят наши схемы: defmodule MediumPhxExample.Audio.Album do use Ecto.Schema schema "audio_albums" do field :name, :string field :release, :utc_datetime timestamps() end end defmodule MediumPhxExample.Audio.Song do use Ecto.Schema schema "audio_songs" do field :duration, :integer field :name, :string field :album_id, :id timestamps() end endВсё за исключением самой схемы исчезло. Нет обязательных полей, никаких функций changeset/2 или каких-либо других. Генератор теперь даже не создает belongs_to для вас. Вы сами управляете связями ваших схем. Итак, теперь это довольно ясно: схема — не место для вашей бизнес-логики. Всё это обрабатывается контекстом, который выглядит следующим образом: defmodule MediumPhxExample.Audio do @moduledoc """ The boundary for the Audio system. """ import Ecto.{Query, Changeset}, warn: false alias MediumPhxExample.Repo alias MediumPhxExample.Audio.Album def list_albums do Repo.all(Album) end def get_album!(id), do: Repo.get!(Album, id) def create_album(attrs \\ %{}) do %Album{} |> album_changeset(attrs) |> Repo.insert() end # ... defp album_changeset(%Album{} = album, attrs) do album |> cast(attrs, [:name, :release]) |> validate_required([:name, :release]) end alias MediumPhxExample.Audio.Song def list_songs do Repo.all(Song) end def get_song!(id), do: Repo.get!(Song, id) def create_song(attrs \\ %{}) do %Song{} |> song_changeset(attrs) |> Repo.insert() end # ... defp song_changeset(%Song{} = song, attrs) do song |> cast(attrs, [:name, :duration]) |> validate_required([:name, :duration]) end endСам вид контекста отправляет ясный посыл: вот место, где нужно поместить свой код! Но будьте осторожны, файлы контекста могут разрастись. Разделите их на несколько модулей в таком случае. Использование контроллераРаньше у нас было много кода в контроллере по-умолчанию и разработчику было легко расширить шаблонный код. Здесь появляется пятое изменение. Начиная с нового выпуска, шаблонный код в контроллере был уменьшен и реорганизован: defmodule MediumPhxExample.Web.AlbumController do use MediumPhxExample.Web, :controller alias MediumPhxExample.Audio alias MediumPhxExample.Audio.Album action_fallback MediumPhxExample.Web.FallbackController # ... def update(conn, %{"id" => id, "album" => album_params}) do album = Audio.get_album!(id) with {:ok, %Album{} = album} put_status(:unprocessable_entity) |> render(MediumPhxExample.Web.ChangesetView, "error.json", changeset: changeset) end def call(conn, {:error, :not_found}) do conn |> put_status(:not_found) |> render(MediumPhxExample.Web.ErrorView, :"404") end endЧто происходит, когда результат из Audio.update_album(album, album_params) не соответствует {:ok, %Album{} = album}? В этой ситуации вызывается контроллер, определенный в action_fallback. И будет выбран правильный call/2, что в свою очередь возвращает правильный ответ. Легко и приятно. Никаких обработок исключений в контроллере. ЗаключениеВнесенные изменения весьма интересны. Их много, они все сфокусированы на том, чтобы загубить старые привычки программистов, которые пришли из других языков программирования. И новые изменения стараются пополнить философию Phoenix-Way новыми практиками. Надеюсь, эта статья была полезна и побудила вас использовать Phoenix Framework по максимуму. Заходите ко мне на GitHub. Благодарим Никиту за подготовку перевода своей собственной оригинальной статьи и с радостью публикуем материал на Хабре. Никита представляет сообщество ElixirLangMoscow, которое организует митапы по Эликсиру в Москве, а также является активным контрибьютером в опенсорс и вносит значительный вклад в наше сообщество Вунш. На сайте вас ждут 3 десятка тематических статей, еженедельная рассылка и новости из мира Эликсира. А для вопросов у нас есть чат в Телеграме с отличными участниками. |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |